package cn.jsj.education.file.management.impl;

import cn.hutool.core.io.FileTypeUtil;
import cn.hutool.core.io.FileUtil;
import cn.hutool.core.io.file.FileReader;
import cn.hutool.core.util.IdUtil;
import cn.jsj.education.exception.instance.CustomRuntimeException;
import cn.jsj.education.file.management.FileService;
import cn.jsj.education.file.management.impl.dao.FileLocationDao;
import cn.jsj.education.file.management.impl.entity.FileLocationEntity;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.stereotype.Service;
import org.springframework.web.multipart.MultipartFile;
import javax.validation.constraints.NotBlank;
import java.io.*;

/**
 * 文件服务模块实现类
 *
 * @author 卢斌
 */
@Slf4j
@Service("FileService")
public class FileServiceLocationImpl implements FileService {

    @Value("${file.location.base-url}")
    private String fileBasePath;

    private final FileLocationDao fileLocationDao;

    @Autowired
    public FileServiceLocationImpl(FileLocationDao fileLocationDao) {
        this.fileLocationDao = fileLocationDao;
    }

    /**
     * 保存MultipartFile对象到文件服务模块，返回文件唯一标识
     *
     * @param multipartFile 文件对象
     * @return 文件唯一标识
     */
    @Override
    public String saveMultiPartFile(MultipartFile multipartFile) {
        if (multipartFile.isEmpty()) {
            throw new CustomRuntimeException("无效的文件");
        }

        // 获取文件类型，fileType为文件后缀名
        String fileType = getFileSuffixNameInMultipart(multipartFile);

        // 检查文件类型是否支持
        if (! checkFileTypeIsSupported(fileType)) {
            throw new CustomRuntimeException("系统不支持的文件类型");
        }

        String primaryKey = IdUtil.simpleUUID();

        // 构造一个相对路径，文件保存在基础路径 + 相对路径的绝对路径处
        String relativePath = this.buildAimFileSavePath(primaryKey, fileType);
        try {
            log.info("开始保存用户文件,识别号为：{}, 保存目标位置为：{}", primaryKey, this.fileBasePath + relativePath);
            multipartFile.transferTo(buildAbsoluteFilePath(relativePath));
            log.info("文件保存成功，识别号为：{}", primaryKey);
        } catch (IOException e) {
            e.printStackTrace();
            throw new CustomRuntimeException("文件保存失败");
        }

        // 将文件存放的位置信息保存到数据库中
        FileLocationEntity fileLocationInsertBody = new FileLocationEntity();
        fileLocationInsertBody.setSavePath(relativePath);
        fileLocationInsertBody.setPrimaryKey(primaryKey);
        fileLocationDao.insertFileLocationEntity(fileLocationInsertBody);
        return primaryKey;
    }

    /**
     * 保存服务器本地路径文件到文件服务模块，返回文件唯一标识
     *
     * @param locationFilePath 服务器本地路径
     * @return 文件唯一标识
     */
    @Override
    public String saveLocationFile(File locationFilePath) {
        if (! locationFilePath.exists()) {
            throw new CustomRuntimeException("无效的文件位置");
        }
        if (locationFilePath.isDirectory()) {
            throw new CustomRuntimeException("请指定某一具体文件，系统不支持使用文件夹");
        }
        String fileType = FileTypeUtil.getType(locationFilePath);
        log.info("文件类型为：{}", fileType);
        // 检查文件类型是否支持
        if (! checkFileTypeIsSupported(fileType)) {
            throw new CustomRuntimeException("系统不支持的文件类型");
        }

        String primaryKey = IdUtil.simpleUUID();

        // 构造一个相对路径，文件保存在基础路径 + 相对路径的绝对路径处
        String relativePath = this.buildAimFileSavePath(primaryKey, fileType);
        FileUtil.copy(locationFilePath, new File(this.fileBasePath + relativePath), true);

        // 将文件存放的位置信息保存到数据库中
        FileLocationEntity fileLocationInsertBody = new FileLocationEntity();
        fileLocationInsertBody.setSavePath(relativePath);
        fileLocationInsertBody.setPrimaryKey(primaryKey);
        fileLocationDao.insertFileLocationEntity(fileLocationInsertBody);
        return primaryKey;
    }

    /**
     * 通过文件唯一标识获取文件流
     *
     * @param primaryKey 文件唯一标识
     * @return fileStream
     */
    @Override
    public BufferedInputStream readFileStream(String primaryKey) {
        String fileRelativePath = fileLocationDao.getRelativePathByPrimaryKey(primaryKey);
        File aimFile = buildAbsoluteFilePath(fileRelativePath);
        FileReader fileReader = new FileReader(aimFile);
        return fileReader.getInputStream();
    }

    /**
     * 检查文件是否存在
     *
     * @param primaryKey 文件唯一标识
     * @return true 存在
     */
    @Override
    public boolean existFile(@NotBlank String primaryKey) {
        return false;
    }

    /**
     * 获取文件服务模块中某一文件的类型，并返回大写后缀名。如果文件不存在则返回null
     *
     * @param primaryKey 文件唯一标识
     * @return 文件大写后缀名
     */
    @Override
    public String getFileSuffixName(@NotBlank String primaryKey) {
        return null;
    }

    /**
     * 获取文件类型，并返回大写后缀名。
     *
     * @param multipartFile 文件实体
     * @return 文件后缀名
     */
    @Override
    public String getFileSuffixNameInMultipart(MultipartFile multipartFile) {
        return multipartFile.getContentType();
    }

    /**
     * 文件系统是否支持该文件类型
     *
     * @param fileSuffix 文件后缀名
     * @return 是否支持
     */
    @Override
    public boolean checkFileTypeIsSupported(String fileSuffix) {
        return true;
    }


    private String buildAimFileSavePath(String primaryKey, String suffixName) {
        long currentMillisecond = System.currentTimeMillis();
        long randomNumber = Math.round(Math.random() * 10);
        long timeLimitNumber = currentMillisecond % 16;
        return randomNumber + "/" + timeLimitNumber + "/" + primaryKey + "." + suffixName;
    }

    private File buildAbsoluteFilePath(String relativePath) {
        return new File(this.fileBasePath + relativePath);
    }

}
